This is an R Markdown Notebook.

Based on a Udemy Class (https://www.udemy.com/practical-data-science-analyzing-stock-market-data-with-r/learn/lecture/3407770#overview)

using quantmod from Cran (https://cran.r-project.org/web/packages/quantmod/)

# install.packages('quantmod')
library(quantmod)
?quantmod

We’ll use Yahoo here but Google Finance is also supported. (https://finance.yahoo.com/lookup/)

We’ll call multiple symbols at once by creating a vector of symbols and then pass the basket to the getSymbols function.

symbolBasket <- c('AAPL', 'AMZN', 'BRK-B', 'SPY')
getSymbols(symbolBasket , src='yahoo')
[1] "AAPL"  "AMZN"  "BRK-B" "SPY"  

Summary will show us basics

summary(`BRK-B`)
     Index              BRK-B.Open       BRK-B.High      BRK-B.Low     
 Min.   :2007-01-03   Min.   : 45.08   Min.   : 47.7   Min.   : 44.82  
 1st Qu.:2010-02-03   1st Qu.: 78.65   1st Qu.: 79.3   1st Qu.: 77.94  
 Median :2013-03-07   Median :102.14   Median :102.7   Median :101.89  
 Mean   :2013-03-06   Mean   :116.22   Mean   :117.0   Mean   :115.31  
 3rd Qu.:2016-04-07   3rd Qu.:145.10   3rd Qu.:145.7   3rd Qu.:144.19  
 Max.   :2019-05-10   Max.   :224.00   Max.   :224.1   Max.   :221.30  
  BRK-B.Close      BRK-B.Volume       BRK-B.Adjusted  
 Min.   : 46.00   Min.   :   175000   Min.   : 46.00  
 1st Qu.: 78.78   1st Qu.:  2240325   1st Qu.: 78.78  
 Median :102.25   Median :  3295750   Median :102.25  
 Mean   :116.20   Mean   :  3707459   Mean   :116.20  
 3rd Qu.:144.92   3rd Qu.:  4388225   3rd Qu.:144.92  
 Max.   :223.76   Max.   :316134200   Max.   :223.76  

Now lets group them all together for a future comparison. Trick here is we might not have the same amount of data due to age of stocks we’ll use the XTS nature of the data to hook them together. In this case we have all data all dates.

NOTE: BRK-B IS ODD SO IT NEEDS THE `` AROUND IT

symbolBasketGrp <- data.frame(as.xts(merge(AAPL, AMZN, `BRK-B`, SPY)))

charting starts here

Lecture 4 (https://www.udemy.com/practical-data-science-analyzing-stock-market-data-with-r/learn/lecture/3407770#questions/7085910)

names(`BRK-B`)
[1] "BRK-B.Open"     "BRK-B.High"     "BRK-B.Low"      "BRK-B.Close"   
[5] "BRK-B.Volume"   "BRK-B.Adjusted"
plot(`BRK-B`$`BRK-B.Open`)

To view some variation on this a bit we can use this code;

lineChart(`BRK-B`$`BRK-B.Open`, line.type = 'h', theme = 'white', TA = NULL)

To see volumes; remove the TA

lineChart(`BRK-B`, line.type = 'h', theme = 'white')

Barchart, type allows for high, low, close ;)

barChart(`BRK-B`, bar.type = 'hlc', TA = NULL)

Now just a subset and in candle sticks

candleChart(`BRK-B`, TA=NULL, subset = '2019')

?candleChart

Not run:

getSymbols(“YHOO”) chartSeries(YHOO) chartSeries(YHOO, subset=‘last 4 months’) chartSeries(YHOO, subset=‘2007::2008-01’) chartSeries(YHOO,theme=chartTheme(‘white’)) chartSeries(YHOO,TA=NULL) #no volume chartSeries(YHOO,TA=c(addVo(),addBBands())) #add volume and Bollinger Bands from TTR

NOTE there are a ton of add… items that are very slick

addMACD() # add MACD indicator to current chart

setTA() chartSeries(YHOO) #draws chart again, this time will all indicators present

End(Not run)

Add MACD

candleChart(`BRK-B`, TA=c(addMACD(),addVo()), subset = '2019')

?addMACD

?addMACD()

addMACD(fast = 12, slow = 26, signal = 9, type = “EMA”, histogram = TRUE, col)

Various ways to dictate time, in this case everything after… Also added in ADX

candleChart(`BRK-B`, TA=c(addMACD(),addADX()), subset = '2018-01::')

Various ways to dictate time, in this case everything in between… Background is the ‘Theme’

candleChart(AAPL , TA=c(addMACD()), subset = '2018-01::2018-05', theme = 'white')

Various options for theme’s overall

candleChart(AAPL , TA=c(addMACD()), subset = '2019-01::', theme = chartTheme('white', up.col='green',dn.col='darkred'))

chartSeries is another cool way to do this same stuff

?chartSeries
chartSeries(AAPL, 
            type = c("auto", "candlesticks"), 
            subset = '2019-01::',
            show.grid = TRUE,
            major.ticks='auto', minor.ticks=TRUE,
            TA=c(addMACD(),addVo()))

chartSeries(AAPL, 
            type = c("auto", "candlesticks"), 
            subset = '2018-01::',
            show.grid = TRUE,
            major.ticks='auto', minor.ticks=TRUE,
            multi.col = TRUE,
            TA=c(addMACD(),addVo()))

chartSeries(`BRK-B`, 
            type = c("auto", "matchsticks"), 
            subset = '2018-01::',
            show.grid = TRUE,
            major.ticks='auto', minor.ticks=TRUE,
            multi.col = TRUE,
            TA=c(addMACD(),addVo()))

Adding indicators starts here - although we jumped ahead up above

Lecture 5 (https://www.udemy.com/practical-data-science-analyzing-stock-market-data-with-r/learn/lecture/3407812#questions/7085910)

We’ll use TTR, which is installed with quantmod, if not you can install TTR TTR - Technical Trading Rules So Cool!

?TTR

Let’s do a Exponential Moving Average

chartSeries(`BRK-B`, 
            type = c("auto", "matchsticks"), 
            subset = '2018-01::',
            show.grid = TRUE,
            major.ticks='auto', minor.ticks=TRUE,
            multi.col = FALSE,
            TA=c(addMACD(),addVo(),addEMA(n=200,col = 'blue'),addEMA(n=50,col = 'red'),addSMA(n=22,col = 'green'),
            addROC(n=200,col = 'blue'),addROC(n=50,col = 'red'),addROC(n=22,col = 'green'))) # rate of change

Bollinger bands Basics; addBBands(n = 20, sd = 2, maType = “SMA”, draw = ‘bands’, on = -1)

?addBBands

Experimental BBands “The primary addition to this function call over the TTR version is in the draw argument. ‘bands’ will draw standard Bollinger Bands, ‘percent’ will draw Bollinger %b and ‘width’ will draw Bolinger Bands Width. The last two will be drawn in new figure regions.”

?add_BBands
chartSeries(SPY, theme="white",
 TA="addVo();addBBands();addCCI()", subset = '2018-01::')

chartSeries(`BRK-B`, theme="white",
 TA="addVo();addBBands();addCCI()", subset = '2018-01::')

CUSTOM INDICATOR

Start with a simple chart.

chartSeries(SPY, theme=chartTheme('white'), up.col="black",
 dn.col="black")

CREATE two vectors as a time series to make a Exponential Moving Average. EMA is like a SMA except it gives more weight to the recent activity and that means it tends to mimic the market a little better.

here we create a 20 period EMA

SPY.EMA.20<- EMA(SPY$SPY.Close, n=20)

here we create a 100 period EMA

SPY.EMA.100<- EMA(SPY$SPY.Close, n=100)

Now let’s add them to our chart.

chartSeries(SPY, 
            theme=chartTheme('white'),
            up.col="black",
            dn.col="black")

            addTA(SPY.EMA.20, on=1, col = "green")

            addTA(SPY.EMA.100, on=1, col = "blue")

?chartSeries
?TTR

Dealing with Berk - errr

BRKB <- as.xts(`BRK-B`)
names(BRKB)
[1] "BRK-B.Open"     "BRK-B.High"     "BRK-B.Low"      "BRK-B.Close"   
[5] "BRK-B.Volume"   "BRK-B.Adjusted"
names(BRKB) <- c("BRKB.Open"   ,  "BRKB.High"   ,  "BRKB.Low"   ,   "BRKB.Close"  ,  "BRKB.Volume",  "BRKB.Adjusted")
names(BRKB)
[1] "BRKB.Open"     "BRKB.High"     "BRKB.Low"      "BRKB.Close"    "BRKB.Volume"  
[6] "BRKB.Adjusted"
BRKB.EMA.20<- EMA(BRKB$BRKB.Close, n=20)
BRKB.EMA.50<- EMA(BRKB$BRKB.Close, n=50)
BRKB.EMA.100<- EMA(BRKB$BRKB.Close, n=100)
BRKB.EMA.200<- EMA(BRKB$BRKB.Close, n=200)
chartSeries(`BRK-B`, 
            type = c("auto", "matchsticks"), 
            subset = '2018-01::',
            show.grid = TRUE,
            major.ticks='auto', minor.ticks=TRUE,
            multi.col = TRUE,
            theme = chartTheme("white"),
            TA=c(addMACD(),addVo(),addADX(n = 14, maType = "EMA")))

            addTA(BRKB.EMA.20, on=1, col = "green")

            addTA(BRKB.EMA.50, on=1, col = "blue")

            addTA(BRKB.EMA.100, on=1, col = "yellow")

            addTA(BRKB.EMA.200, on=1, col = "red")

            addTA(BRKB.EMA.100 - BRKB.EMA.200, col = "black",
                  type='h', legend="100-200 EMA")

Create a SMA from scratch to understand

Lecture 7 (https://www.udemy.com/practical-data-science-analyzing-stock-market-data-with-r/learn/lecture/3410012#questions)

library(quantmod)
getSymbols(c('QQQ'), src='yahoo')
[1] "QQQ"

Plot the graph of the triple Q’s

plot(QQQ$QQQ.Close)

We choose a period, which sets the number of points of data that will be required to create one point of raw data (averaged) to get a point of data in our visualization. The price_vector is the group of data we want to use.

period <- 100
price_vector <- QQQ$QQQ.Close
length(price_vector)
[1] 3110

Now we need a vector to put our values into, so we’ll define an empty vector that we’ll then make a loop through our price_vector to fille the vector

moving_average_vector <- c()

Let’s look at sequence to understand : and ,

seq(5:10) # note that sequence gives us the number of values between 5 and 10 ;) 
[1] 1 2 3 4 5 6
seq(5,10) # this sequence gives us from 5 to 10 
[1]  5  6  7  8  9 10
for (ind in seq(period:length(price_vector))) { # so, period starts at 1
        print(ind)
        break # to stop it at the first go
}
[1] 1

The ‘Loop’. Let’s have a look at how this comes together.

for (ind in seq(period:length(price_vector))) { # so, period starts at 1
        print(ind)
        break # to stop it at the first go
}
[1] 1

Let’s fix that with a comma

for (ind in seq(period,length(price_vector))) { # period is 100
        print(ind)
        break # to stop it at the first go
}
[1] 100

Now, because we want to use 100 values as the period we need to start at 100 + 1, and we need parens. And we now can start the code to assign the values to the new empty vector.

for (ind in seq((period + 1),length(price_vector))) {
    moving_average_vector <- c(moving_average_vector, # here we are saying add the mean to 
                                                      # the moving_average_vector
                               mean(price_vector[(ind - period:ind)])) # 
}
head(moving_average_vector)
[1] 43.24000 43.65000 43.71667 43.75750 43.82600 43.95833
tail(moving_average_vector)
[1] 82.46752 82.49433 82.52131 82.54874 82.57618 82.60226
summary(moving_average_vector)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  41.98   45.02   49.16   54.38   63.42   82.60 
moving_average_vector[1:100] # show 100 items
  [1] 43.24000 43.65000 43.71667 43.75750 43.82600 43.95833 44.11857 44.26750 44.38000
 [10] 44.43300 44.40091 44.38167 44.32846 44.27500 44.27667 44.24250 44.20294 44.17000
 [19] 44.14158 44.13800 44.13143 44.13273 44.13217 44.12917 44.14240 44.15423 44.14407
 [28] 44.12857 44.11931 44.13533 44.15871 44.17594 44.20242 44.23147 44.26543 44.29306
 [37] 44.31324 44.28368 44.25923 44.23100 44.18829 44.13976 44.10977 44.07727 44.05267
 [46] 44.02826 44.01085 43.97667 43.95653 43.93520 43.91353 43.90115 43.89509 43.90481
 [55] 43.91127 43.91500 43.92175 43.92293 43.91610 43.91033 43.90410 43.89903 43.90317
 [64] 43.91000 43.92000 43.92803 43.93925 43.94324 43.95217 43.96214 43.97761 43.99403
 [73] 44.00795 44.02338 44.04173 44.06053 44.08182 44.11038 44.14127 44.17163 44.19370
 [82] 44.21476 44.24133 44.26929 44.29706 44.32419 44.35184 44.38000 44.40034 44.42678
 [91] 44.44912 44.46707 44.48946 44.50904 44.53221 44.55802 44.58371 44.60663 44.62232
[100] 44.64060

We can see from above what has happened. We have, started with the first 100 recalculated the mean with each 100 previous values, all the way to the end.

Let’s graph a few things.

par(mfrow=c(2,1)) # gives us two graphs, one on top of another
plot(QQQ$QQQ.Close)
plot(moving_average_vector, type = 'l', col = 'red', lwd=3,
      main = paste('SMA', period))

Let’s check the length. We can see that we gave up that 100 to calculate the mean given the period of 100. This means however that we cannot graph the two against one another because they are different sizes.

length(price_vector)
[1] 3110
length(moving_average_vector)
[1] 3010

Let’s fix that; We’ll play a little trick and fill in those first 100 characters with NA using the repeat function. Here we say give me 100 NA’s

rep(NA, period)
  [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
 [28] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
 [55] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
 [82] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA

So, instead of starting moving_average_vector as a blank vector we’ll start with 100 NA’s, or whatever the period is.

period <- 100
price_vector <- QQQ$QQQ.Close
moving_average_vector <- c(rep(NA, period))
for (ind in seq((period + 1),length(price_vector))) {
    moving_average_vector <- c(moving_average_vector, # here we are saying add the mean to 
                                                      # the moving_average_vector
                               mean(price_vector[(ind - period):ind])) # 
}

Check the length. Bingo.

length(moving_average_vector)
[1] 3110
length(price_vector)
[1] 3110

Now we can add our moving average to the XTS object of the QQQ’s.

QQQ$QQQ.Close.SMA <- moving_average_vector
names(QQQ)
[1] "QQQ.Open"      "QQQ.High"      "QQQ.Low"       "QQQ.Close"     "QQQ.Volume"   
[6] "QQQ.Adjusted"  "QQQ.Close.SMA"

Let’s graph the close and the new SMA.

plot(QQQ$QQQ.Close)

lines(QQQ$QQQ.Close.SMA, type = 'l', col = 'red', lwd = 6)

Let’s call the same thing from TTR package ;) We’ll use fresh data. Same thing 2 lines of code.

getSymbols(c('QQQ'), src = 'yahoo')
[1] "QQQ"
chartSeries(QQQ, theme='white', TA="addSMA(100)")

================== NOTES on Syntax below here two options for code in MarkDown

rr getSymbols(‘^GSPC’, src=‘yahoo’)

[1] \^GSPC\

rr getSymbols(‘^GSPC’, src=‘yahoo’)

[1] \^GSPC\

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUHJhY3RpY2FsIERhdGEgU2NpZW5jZTogQW5hbHl6aW5nIFN0b2NrIE1hcmtldCBEYXRhIHdpdGggUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIAoKQmFzZWQgb24gYSBVZGVteSBDbGFzcwooaHR0cHM6Ly93d3cudWRlbXkuY29tL3ByYWN0aWNhbC1kYXRhLXNjaWVuY2UtYW5hbHl6aW5nLXN0b2NrLW1hcmtldC1kYXRhLXdpdGgtci9sZWFybi9sZWN0dXJlLzM0MDc3NzAjb3ZlcnZpZXcpCgp1c2luZyBxdWFudG1vZCBmcm9tIENyYW4KKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9xdWFudG1vZC8pCgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygncXVhbnRtb2QnKQpsaWJyYXJ5KHF1YW50bW9kKQpgYGAKCmBgYHtyIGVjaG89VFJVRX0KP3F1YW50bW9kCmBgYAoKV2UnbGwgdXNlIFlhaG9vIGhlcmUgYnV0IEdvb2dsZSBGaW5hbmNlIGlzIGFsc28gc3VwcG9ydGVkLiAKKGh0dHBzOi8vZmluYW5jZS55YWhvby5jb20vbG9va3VwLykKCldlJ2xsIGNhbGwgbXVsdGlwbGUgc3ltYm9scyBhdCBvbmNlIGJ5IGNyZWF0aW5nIGEgdmVjdG9yIG9mIHN5bWJvbHMKYW5kIHRoZW4gcGFzcyB0aGUgYmFza2V0IHRvIHRoZSBnZXRTeW1ib2xzIGZ1bmN0aW9uLgoKYGBge3IgIH0Kc3ltYm9sQmFza2V0IDwtIGMoJ0FBUEwnLCAnQU1aTicsICdCUkstQicsICdTUFknKQpnZXRTeW1ib2xzKHN5bWJvbEJhc2tldCAsIHNyYz0neWFob28nKQpgYGAKClN1bW1hcnkgd2lsbCBzaG93IHVzIGJhc2ljcwpgYGB7ciAsIGluY2x1ZGU9VFJVRX0Kc3VtbWFyeShgQlJLLUJgKQpgYGAKCk5vdyBsZXRzIGdyb3VwIHRoZW0gYWxsIHRvZ2V0aGVyIGZvciBhIGZ1dHVyZSBjb21wYXJpc29uLiAKVHJpY2sgaGVyZSBpcyB3ZSBtaWdodCBub3QgaGF2ZSB0aGUgc2FtZSBhbW91bnQgb2YgZGF0YSBkdWUgdG8gYWdlIG9mIHN0b2Nrcwp3ZSdsbCB1c2UgdGhlIFhUUyBuYXR1cmUgb2YgdGhlIGRhdGEgdG8gaG9vayB0aGVtIHRvZ2V0aGVyLiBJbiB0aGlzIGNhc2Ugd2UgaGF2ZSBhbGwgZGF0YSBhbGwgZGF0ZXMuIAoKTk9URTogQlJLLUIgSVMgT0REIFNPIElUIE5FRURTIFRIRSBgYCBBUk9VTkQgSVQKCmBgYHtyICB9CnN5bWJvbEJhc2tldEdycCA8LSBkYXRhLmZyYW1lKGFzLnh0cyhtZXJnZShBQVBMLCBBTVpOLCBgQlJLLUJgLCBTUFkpKSkKYGBgCgoKIyMjIGNoYXJ0aW5nIHN0YXJ0cyBoZXJlCgpMZWN0dXJlIDQgKGh0dHBzOi8vd3d3LnVkZW15LmNvbS9wcmFjdGljYWwtZGF0YS1zY2llbmNlLWFuYWx5emluZy1zdG9jay1tYXJrZXQtZGF0YS13aXRoLXIvbGVhcm4vbGVjdHVyZS8zNDA3NzcwI3F1ZXN0aW9ucy83MDg1OTEwKQoKYGBge3IgIH0KbmFtZXMoYEJSSy1CYCkKYGBgCgpgYGB7ciBlY2hvPVRSVUV9CnBsb3QoYEJSSy1CYCRgQlJLLUIuT3BlbmApCmBgYAoKVG8gdmlldyBzb21lIHZhcmlhdGlvbiBvbiB0aGlzIGEgYml0IHdlIGNhbiB1c2UgdGhpcyBjb2RlOwoKYGBge3IgIH0KbGluZUNoYXJ0KGBCUkstQmAkYEJSSy1CLk9wZW5gLCBsaW5lLnR5cGUgPSAnaCcsIHRoZW1lID0gJ3doaXRlJywgVEEgPSBOVUxMKQpgYGAKClRvIHNlZSB2b2x1bWVzOwpyZW1vdmUgdGhlIFRBCgpgYGB7ciAgfQpsaW5lQ2hhcnQoYEJSSy1CYCwgbGluZS50eXBlID0gJ2gnLCB0aGVtZSA9ICd3aGl0ZScpCmBgYAoKQmFyY2hhcnQsIHR5cGUgYWxsb3dzIGZvciBoaWdoLCBsb3csIGNsb3NlIDspIAoKYGBge3IgIH0KYmFyQ2hhcnQoYEJSSy1CYCwgYmFyLnR5cGUgPSAnaGxjJywgVEEgPSBOVUxMKQpgYGAKCk5vdyBqdXN0IGEgc3Vic2V0IGFuZCBpbiBjYW5kbGUgc3RpY2tzCmBgYHtyIGVjaG89VFJVRX0KY2FuZGxlQ2hhcnQoYEJSSy1CYCwgVEE9TlVMTCwgc3Vic2V0ID0gJzIwMTknKQpgYGAKCgpgYGB7ciAgfQo/Y2FuZGxlQ2hhcnQKYGBgCgojIyBOb3QgcnVuOiAKZ2V0U3ltYm9scygiWUhPTyIpCmNoYXJ0U2VyaWVzKFlIT08pCmNoYXJ0U2VyaWVzKFlIT08sIHN1YnNldD0nbGFzdCA0IG1vbnRocycpCmNoYXJ0U2VyaWVzKFlIT08sIHN1YnNldD0nMjAwNzo6MjAwOC0wMScpCmNoYXJ0U2VyaWVzKFlIT08sdGhlbWU9Y2hhcnRUaGVtZSgnd2hpdGUnKSkKY2hhcnRTZXJpZXMoWUhPTyxUQT1OVUxMKSAgICNubyB2b2x1bWUKY2hhcnRTZXJpZXMoWUhPTyxUQT1jKGFkZFZvKCksYWRkQkJhbmRzKCkpKSAgI2FkZCB2b2x1bWUgYW5kIEJvbGxpbmdlciBCYW5kcyBmcm9tIFRUUgoKTk9URSB0aGVyZSBhcmUgYSB0b24gb2YgYWRkLi4uIGl0ZW1zIHRoYXQgYXJlIHZlcnkgc2xpY2sKCmFkZE1BQ0QoKSAgICMgIGFkZCBNQUNEIGluZGljYXRvciB0byBjdXJyZW50IGNoYXJ0CgpzZXRUQSgpCmNoYXJ0U2VyaWVzKFlIT08pICAgI2RyYXdzIGNoYXJ0IGFnYWluLCB0aGlzIHRpbWUgd2lsbCBhbGwgaW5kaWNhdG9ycyBwcmVzZW50CgojIyBFbmQoTm90IHJ1bikKCkFkZCBNQUNEIAoKYGBge3IgIH0KY2FuZGxlQ2hhcnQoYEJSSy1CYCwgVEE9YyhhZGRNQUNEKCksYWRkVm8oKSksIHN1YnNldCA9ICcyMDE5JykKYGBgCgo/YWRkTUFDRApgYGB7ciAsIGluY2x1ZGU9VFJVRX0KP2FkZE1BQ0QoKQpgYGAKCmFkZE1BQ0QoZmFzdCA9IDEyLCBzbG93ID0gMjYsIHNpZ25hbCA9IDksIHR5cGUgPSAiRU1BIiwgaGlzdG9ncmFtID0gVFJVRSwgY29sKQoKVmFyaW91cyB3YXlzIHRvIGRpY3RhdGUgdGltZSwgaW4gdGhpcyBjYXNlIGV2ZXJ5dGhpbmcgYWZ0ZXIuLi4KQWxzbyBhZGRlZCBpbiBBRFgKYGBge3IgIH0KY2FuZGxlQ2hhcnQoYEJSSy1CYCwgVEE9YyhhZGRNQUNEKCksYWRkQURYKCkpLCBzdWJzZXQgPSAnMjAxOC0wMTo6JykKYGBgCgpWYXJpb3VzIHdheXMgdG8gZGljdGF0ZSB0aW1lLCBpbiB0aGlzIGNhc2UgZXZlcnl0aGluZyBpbiBiZXR3ZWVuLi4uCkJhY2tncm91bmQgaXMgdGhlICdUaGVtZScKCmBgYHtyICB9CmNhbmRsZUNoYXJ0KEFBUEwgLCBUQT1jKGFkZE1BQ0QoKSksIHN1YnNldCA9ICcyMDE4LTAxOjoyMDE4LTA1JywgdGhlbWUgPSAnd2hpdGUnKQpgYGAKClZhcmlvdXMgb3B0aW9ucyBmb3IgdGhlbWUncyBvdmVyYWxsCgpgYGB7ciAgfQpjYW5kbGVDaGFydChBQVBMICwgVEE9YyhhZGRNQUNEKCkpLCBzdWJzZXQgPSAnMjAxOS0wMTo6JywgdGhlbWUgPSBjaGFydFRoZW1lKCd3aGl0ZScsIHVwLmNvbD0nZ3JlZW4nLGRuLmNvbD0nZGFya3JlZCcpKQpgYGAKCmNoYXJ0U2VyaWVzIGlzIGFub3RoZXIgY29vbCB3YXkgdG8gZG8gdGhpcyBzYW1lIHN0dWZmCgpgYGB7ciB9Cj9jaGFydFNlcmllcwpgYGAKCmBgYHtyIH0KY2hhcnRTZXJpZXMoQUFQTCwgCiAgICAgICAgICAgIHR5cGUgPSBjKCJhdXRvIiwgImNhbmRsZXN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTktMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgVEE9YyhhZGRNQUNEKCksYWRkVm8oKSkpCmBgYAoKCmBgYHtyIH0KY2hhcnRTZXJpZXMoQUFQTCwgCiAgICAgICAgICAgIHR5cGUgPSBjKCJhdXRvIiwgImNhbmRsZXN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gVFJVRSwKICAgICAgICAgICAgVEE9YyhhZGRNQUNEKCksYWRkVm8oKSkpCmBgYAoKYGBge3IgfQpjaGFydFNlcmllcyhgQlJLLUJgLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAibWF0Y2hzdGlja3MiKSwgCiAgICAgICAgICAgIHN1YnNldCA9ICcyMDE4LTAxOjonLAogICAgICAgICAgICBzaG93LmdyaWQgPSBUUlVFLAogICAgICAgICAgICBtYWpvci50aWNrcz0nYXV0bycsIG1pbm9yLnRpY2tzPVRSVUUsCiAgICAgICAgICAgIG11bHRpLmNvbCA9IFRSVUUsCiAgICAgICAgICAgIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCkpKQpgYGAKCgojIyMgQWRkaW5nIGluZGljYXRvcnMgc3RhcnRzIGhlcmUgLSBhbHRob3VnaCB3ZSBqdW1wZWQgYWhlYWQgdXAgYWJvdmUKCkxlY3R1cmUgNSAoaHR0cHM6Ly93d3cudWRlbXkuY29tL3ByYWN0aWNhbC1kYXRhLXNjaWVuY2UtYW5hbHl6aW5nLXN0b2NrLW1hcmtldC1kYXRhLXdpdGgtci9sZWFybi9sZWN0dXJlLzM0MDc4MTIjcXVlc3Rpb25zLzcwODU5MTApCgpXZSdsbCB1c2UgVFRSLCB3aGljaCBpcyBpbnN0YWxsZWQgd2l0aCBxdWFudG1vZCwgaWYgbm90IHlvdSBjYW4gaW5zdGFsbCBUVFIKVFRSIC0gVGVjaG5pY2FsIFRyYWRpbmcgUnVsZXMKU28gQ29vbCEKCmBgYHtyICB9Cj9UVFIKYGBgCgpMZXQncyBkbyBhIEV4cG9uZW50aWFsIE1vdmluZyBBdmVyYWdlCgpgYGB7ciB9CmNoYXJ0U2VyaWVzKGBCUkstQmAsIAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gRkFMU0UsCiAgICAgICAgICAgIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCksYWRkRU1BKG49MjAwLGNvbCA9ICdibHVlJyksYWRkRU1BKG49NTAsY29sID0gJ3JlZCcpLGFkZFNNQShuPTIyLGNvbCA9ICdncmVlbicpLAogICAgICAgICAgICBhZGRST0Mobj0yMDAsY29sID0gJ2JsdWUnKSxhZGRST0Mobj01MCxjb2wgPSAncmVkJyksYWRkUk9DKG49MjIsY29sID0gJ2dyZWVuJykpKSAjIHJhdGUgb2YgY2hhbmdlCmBgYAoKQm9sbGluZ2VyIGJhbmRzCkJhc2ljczsKYWRkQkJhbmRzKG4gPSAyMCwgc2QgPSAyLCBtYVR5cGUgPSAiU01BIiwgZHJhdyA9ICdiYW5kcycsIG9uID0gLTEpCgpgYGB7ciAgfQo/YWRkQkJhbmRzCmBgYAoKRXhwZXJpbWVudGFsIEJCYW5kcyAiVGhlIHByaW1hcnkgYWRkaXRpb24gdG8gdGhpcyBmdW5jdGlvbiBjYWxsIG92ZXIgdGhlIFRUUiB2ZXJzaW9uIGlzIGluIHRoZSBkcmF3IGFyZ3VtZW50LiDigJhiYW5kc+KAmSB3aWxsIGRyYXcgc3RhbmRhcmQgQm9sbGluZ2VyIEJhbmRzLCDigJhwZXJjZW504oCZIHdpbGwgZHJhdyBCb2xsaW5nZXIgJWIgYW5kIOKAmHdpZHRo4oCZIHdpbGwgZHJhdyBCb2xpbmdlciBCYW5kcyBXaWR0aC4gVGhlIGxhc3QgdHdvIHdpbGwgYmUgZHJhd24gaW4gbmV3IGZpZ3VyZSByZWdpb25zLiIKYGBge3IgIH0KP2FkZF9CQmFuZHMKYGBgCgpgYGB7ciAgfQpjaGFydFNlcmllcyhTUFksIHRoZW1lPSJ3aGl0ZSIsCiBUQT0iYWRkVm8oKTthZGRCQmFuZHMoKTthZGRDQ0koKSIsIHN1YnNldCA9ICcyMDE4LTAxOjonKQpgYGAKCgpgYGB7ciAgfQpjaGFydFNlcmllcyhgQlJLLUJgLCB0aGVtZT0id2hpdGUiLAogVEE9ImFkZFZvKCk7YWRkQkJhbmRzKCk7YWRkQ0NJKCkiLCBzdWJzZXQgPSAnMjAxOC0wMTo6JykKYGBgCgoKIyMjIENVU1RPTSBJTkRJQ0FUT1IKClN0YXJ0IHdpdGggYSBzaW1wbGUgY2hhcnQuIAoKYGBge3IgfQpjaGFydFNlcmllcyhTUFksIHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJyksIHVwLmNvbD0iYmxhY2siLAogZG4uY29sPSJibGFjayIpCmBgYAoKQ1JFQVRFIHR3byB2ZWN0b3JzIGFzIGEgdGltZSBzZXJpZXMgdG8gbWFrZSBhIEV4cG9uZW50aWFsIE1vdmluZyBBdmVyYWdlLiAKRU1BIGlzIGxpa2UgYSBTTUEgZXhjZXB0IGl0IGdpdmVzIG1vcmUgd2VpZ2h0IHRvIHRoZSByZWNlbnQgYWN0aXZpdHkgYW5kIAp0aGF0IG1lYW5zIGl0IHRlbmRzIHRvIG1pbWljIHRoZSBtYXJrZXQgYSBsaXR0bGUgYmV0dGVyLiAKCmhlcmUgd2UgY3JlYXRlIGEgMjAgcGVyaW9kIEVNQQpgYGB7ciB9ClNQWS5FTUEuMjA8LSBFTUEoU1BZJFNQWS5DbG9zZSwgbj0yMCkKYGBgCmhlcmUgd2UgY3JlYXRlIGEgMTAwIHBlcmlvZCBFTUEKYGBge3IgfQpTUFkuRU1BLjEwMDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTEwMCkKYGBgCgpOb3cgbGV0J3MgYWRkIHRoZW0gdG8gb3VyIGNoYXJ0LgpgYGB7ciB9CmNoYXJ0U2VyaWVzKFNQWSwgCiAgICAgICAgICAgIHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJyksCiAgICAgICAgICAgIHVwLmNvbD0iYmxhY2siLAogICAgICAgICAgICBkbi5jb2w9ImJsYWNrIikKICAgICAgICAgICAgYWRkVEEoU1BZLkVNQS4yMCwgb249MSwgY29sID0gImdyZWVuIikKICAgICAgICAgICAgYWRkVEEoU1BZLkVNQS4xMDAsIG9uPTEsIGNvbCA9ICJibHVlIikKYGBgCgpgYGB7ciB9Cj9jaGFydFNlcmllcwpgYGAKCmBgYHtyIH0KP1RUUgpgYGAKCkRlYWxpbmcgd2l0aCBCZXJrIC0gZXJycgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CkJSS0IgPC0gYXMueHRzKGBCUkstQmApCm5hbWVzKEJSS0IpCm5hbWVzKEJSS0IpIDwtIGMoIkJSS0IuT3BlbiIgICAsICAiQlJLQi5IaWdoIiAgICwgICJCUktCLkxvdyIgICAsICAgIkJSS0IuQ2xvc2UiICAsICAiQlJLQi5Wb2x1bWUiLCAgIkJSS0IuQWRqdXN0ZWQiKQpuYW1lcyhCUktCKQoKQlJLQi5FTUEuMjA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTIwKQpCUktCLkVNQS41MDwtIEVNQShCUktCJEJSS0IuQ2xvc2UsIG49NTApCkJSS0IuRU1BLjEwMDwtIEVNQShCUktCJEJSS0IuQ2xvc2UsIG49MTAwKQpCUktCLkVNQS4yMDA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTIwMCkKCmNoYXJ0U2VyaWVzKGBCUkstQmAsIAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gVFJVRSwKICAgICAgICAgICAgdGhlbWUgPSBjaGFydFRoZW1lKCJ3aGl0ZSIpLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpLGFkZEFEWChuID0gMTQsIG1hVHlwZSA9ICJFTUEiKSkpCgogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4yMCwgb249MSwgY29sID0gImdyZWVuIikKICAgICAgICAgICAgYWRkVEEoQlJLQi5FTUEuNTAsIG9uPTEsIGNvbCA9ICJibHVlIikKICAgICAgICAgICAgYWRkVEEoQlJLQi5FTUEuMTAwLCBvbj0xLCBjb2wgPSAieWVsbG93IikKICAgICAgICAgICAgYWRkVEEoQlJLQi5FTUEuMjAwLCBvbj0xLCBjb2wgPSAicmVkIikKICAgICAgICAgICAgYWRkVEEoQlJLQi5FTUEuMTAwIC0gQlJLQi5FTUEuMjAwLCBjb2wgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICB0eXBlPSdoJywgbGVnZW5kPSIxMDAtMjAwIEVNQSIpCmBgYAoKCiMjIyBDcmVhdGUgYSBTTUEgZnJvbSBzY3JhdGNoIHRvIHVuZGVyc3RhbmQKCkxlY3R1cmUgNyAoaHR0cHM6Ly93d3cudWRlbXkuY29tL3ByYWN0aWNhbC1kYXRhLXNjaWVuY2UtYW5hbHl6aW5nLXN0b2NrLW1hcmtldC1kYXRhLXdpdGgtci9sZWFybi9sZWN0dXJlLzM0MTAwMTIjcXVlc3Rpb25zKQoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpsaWJyYXJ5KHF1YW50bW9kKQpnZXRTeW1ib2xzKGMoJ1FRUScpLCBzcmM9J3lhaG9vJykKYGBgCgpQbG90IHRoZSBncmFwaCBvZiB0aGUgdHJpcGxlIFEncwpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBsb3QoUVFRJFFRUS5DbG9zZSkKYGBgCgpXZSBjaG9vc2UgYSBwZXJpb2QsIHdoaWNoIHNldHMgdGhlIG51bWJlciBvZiBwb2ludHMgb2YgZGF0YSB0aGF0IHdpbGwgYmUgcmVxdWlyZWQgdG8gY3JlYXRlIG9uZSBwb2ludCBvZiByYXcgZGF0YSAoYXZlcmFnZWQpIHRvIGdldCBhIHBvaW50IG9mIGRhdGEgaW4gb3VyIHZpc3VhbGl6YXRpb24uIFRoZSBwcmljZV92ZWN0b3IgaXMgdGhlIGdyb3VwIG9mIGRhdGEgd2Ugd2FudCB0byB1c2UuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBlcmlvZCA8LSAxMDAKcHJpY2VfdmVjdG9yIDwtIFFRUSRRUVEuQ2xvc2UKbGVuZ3RoKHByaWNlX3ZlY3RvcikKYGBgCgpOb3cgd2UgbmVlZCBhIHZlY3RvciB0byBwdXQgb3VyIHZhbHVlcyBpbnRvLCBzbyB3ZSdsbCBkZWZpbmUgYW4gZW1wdHkgdmVjdG9yIHRoYXQgd2UnbGwgdGhlbiBtYWtlIGEgbG9vcCB0aHJvdWdoIG91ciBwcmljZV92ZWN0b3IgdG8gZmlsbGUgdGhlIHZlY3RvcgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9Cm1vdmluZ19hdmVyYWdlX3ZlY3RvciA8LSBjKCkKYGBgCgpMZXQncyBsb29rIGF0IHNlcXVlbmNlIHRvIHVuZGVyc3RhbmQgOiBhbmQgLApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnNlcSg1OjEwKSAjIG5vdGUgdGhhdCBzZXF1ZW5jZSBnaXZlcyB1cyB0aGUgbnVtYmVyIG9mIHZhbHVlcyBiZXR3ZWVuIDUgYW5kIDEwIDspIApzZXEoNSwxMCkgIyB0aGlzIHNlcXVlbmNlIGdpdmVzIHVzIGZyb20gNSB0byAxMCAKYGBgCgpUaGUgJ0xvb3AnLiBMZXQncyBoYXZlIGEgbG9vayBhdCBob3cgdGhpcyBjb21lcyB0b2dldGhlci4KYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpmb3IgKGluZCBpbiBzZXEocGVyaW9kOmxlbmd0aChwcmljZV92ZWN0b3IpKSkgeyAjIHNvLCBwZXJpb2Qgc3RhcnRzIGF0IDEgZHVlIHRvIDoKICAgICAgICBwcmludChpbmQpCiAgICAgICAgYnJlYWsgIyB0byBzdG9wIGl0IGF0IHRoZSBmaXJzdCBnbwp9CmBgYAoKTGV0J3MgZml4IHRoYXQgd2l0aCBhIGNvbW1hCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KZm9yIChpbmQgaW4gc2VxKHBlcmlvZCxsZW5ndGgocHJpY2VfdmVjdG9yKSkpIHsgIyBwZXJpb2QgaXMgMTAwCiAgICAgICAgcHJpbnQoaW5kKQogICAgICAgIGJyZWFrICMgdG8gc3RvcCBpdCBhdCB0aGUgZmlyc3QgZ28KfQpgYGAKCk5vdywgYmVjYXVzZSB3ZSB3YW50IHRvIHVzZSAxMDAgdmFsdWVzIGFzIHRoZSBwZXJpb2Qgd2UgbmVlZCB0byBzdGFydCBhdCAxMDAgKyAxLCBhbmQgd2UgbmVlZCBwYXJlbnMuIEFuZCB3ZSBub3cgY2FuIHN0YXJ0IHRoZSBjb2RlIHRvIGFzc2lnbiB0aGUgdmFsdWVzIHRvIHRoZSBuZXcgZW1wdHkgdmVjdG9yLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpmb3IgKGluZCBpbiBzZXEoKHBlcmlvZCArIDEpLGxlbmd0aChwcmljZV92ZWN0b3IpKSkgewogICAgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMobW92aW5nX2F2ZXJhZ2VfdmVjdG9yLCAjIGhlcmUgd2UgYXJlIHNheWluZyBhZGQgdGhlIG1lYW4gdG8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG1vdmluZ19hdmVyYWdlX3ZlY3RvcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihwcmljZV92ZWN0b3JbKGluZCAtIHBlcmlvZDppbmQpXSkpICMgCn0KYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmhlYWQobW92aW5nX2F2ZXJhZ2VfdmVjdG9yKQp0YWlsKG1vdmluZ19hdmVyYWdlX3ZlY3RvcikKc3VtbWFyeShtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCm1vdmluZ19hdmVyYWdlX3ZlY3RvclsxOjEwMF0gIyBzaG93IDEwMCBpdGVtcwpgYGAKCldlIGNhbiBzZWUgZnJvbSBhYm92ZSB3aGF0IGhhcyBoYXBwZW5lZC4gV2UgaGF2ZSwgc3RhcnRlZCB3aXRoIHRoZSBmaXJzdCAxMDAgcmVjYWxjdWxhdGVkIHRoZSBtZWFuIHdpdGggZWFjaCAxMDAgcHJldmlvdXMgdmFsdWVzLCBhbGwgdGhlIHdheSB0byB0aGUgZW5kLiAKCkxldCdzIGdyYXBoIGEgZmV3IHRoaW5ncy4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGFyKG1mcm93PWMoMiwxKSkgIyBnaXZlcyB1cyB0d28gZ3JhcGhzLCBvbmUgb24gdG9wIG9mIGFub3RoZXIKcGxvdChRUVEkUVFRLkNsb3NlKQpwbG90KG1vdmluZ19hdmVyYWdlX3ZlY3RvciwgdHlwZSA9ICdsJywgY29sID0gJ3JlZCcsIGx3ZD0zLAogICAgICBtYWluID0gcGFzdGUoJ1NNQScsIHBlcmlvZCkpCmBgYAoKTGV0J3MgY2hlY2sgdGhlIGxlbmd0aC4gV2UgY2FuIHNlZSB0aGF0IHdlIGdhdmUgdXAgdGhhdCAxMDAgdG8gY2FsY3VsYXRlIHRoZSBtZWFuIGdpdmVuIHRoZSBwZXJpb2Qgb2YgMTAwLiBUaGlzIG1lYW5zIGhvd2V2ZXIgdGhhdCB3ZSBjYW5ub3QgZ3JhcGggdGhlIHR3byBhZ2FpbnN0IG9uZSBhbm90aGVyIGJlY2F1c2UgdGhleSBhcmUgZGlmZmVyZW50IHNpemVzLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpsZW5ndGgocHJpY2VfdmVjdG9yKQpsZW5ndGgobW92aW5nX2F2ZXJhZ2VfdmVjdG9yKQpgYGAKCkxldCdzIGZpeCB0aGF0OyBXZSdsbCBwbGF5IGEgbGl0dGxlIHRyaWNrIGFuZCBmaWxsIGluIHRob3NlIGZpcnN0IDEwMCBjaGFyYWN0ZXJzIHdpdGggTkEgdXNpbmcgdGhlIHJlcGVhdCBmdW5jdGlvbi4gSGVyZSB3ZSBzYXkgZ2l2ZSBtZSAxMDAgTkEncwpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnJlcChOQSwgcGVyaW9kKQpgYGAKClNvLCBpbnN0ZWFkIG9mIHN0YXJ0aW5nIG1vdmluZ19hdmVyYWdlX3ZlY3RvciBhcyBhIGJsYW5rIHZlY3RvciB3ZSdsbCBzdGFydCB3aXRoIDEwMCBOQSdzLCBvciB3aGF0ZXZlciB0aGUgcGVyaW9kIGlzLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpwZXJpb2QgPC0gMTAwCnByaWNlX3ZlY3RvciA8LSBRUVEkUVFRLkNsb3NlCm1vdmluZ19hdmVyYWdlX3ZlY3RvciA8LSBjKHJlcChOQSwgcGVyaW9kKSkKZm9yIChpbmQgaW4gc2VxKChwZXJpb2QgKyAxKSxsZW5ndGgocHJpY2VfdmVjdG9yKSkpIHsKICAgIG1vdmluZ19hdmVyYWdlX3ZlY3RvciA8LSBjKG1vdmluZ19hdmVyYWdlX3ZlY3RvciwgIyBoZXJlIHdlIGFyZSBzYXlpbmcgYWRkIHRoZSBtZWFuIHRvIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBtb3ZpbmdfYXZlcmFnZV92ZWN0b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocHJpY2VfdmVjdG9yWyhpbmQgLSBwZXJpb2QpOmluZF0pKSAjIAp9CmBgYAoKQ2hlY2sgdGhlIGxlbmd0aC4gQmluZ28uIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9Cmxlbmd0aChtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCmxlbmd0aChwcmljZV92ZWN0b3IpCmBgYAoKTm93IHdlIGNhbiBhZGQgb3VyIG1vdmluZyBhdmVyYWdlIHRvIHRoZSBYVFMgb2JqZWN0IG9mIHRoZSBRUVEncy4KYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpRUVEkUVFRLkNsb3NlLlNNQSA8LSBtb3ZpbmdfYXZlcmFnZV92ZWN0b3IKbmFtZXMoUVFRKQpgYGAKCkxldCdzIGdyYXBoIHRoZSBjbG9zZSBhbmQgdGhlIG5ldyBTTUEuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBsb3QoUVFRJFFRUS5DbG9zZSkKbGluZXMoUVFRJFFRUS5DbG9zZS5TTUEsIHR5cGUgPSAnbCcsIGNvbCA9ICdyZWQnLCBsd2QgPSA2KQpgYGAKCkxldCdzIGNhbGwgdGhlIHNhbWUgdGhpbmcgZnJvbSBUVFIgcGFja2FnZSA7KSAKV2UnbGwgdXNlIGZyZXNoIGRhdGEuIFNhbWUgdGhpbmcgMiBsaW5lcyBvZiBjb2RlLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpnZXRTeW1ib2xzKGMoJ1FRUScpLCBzcmMgPSAneWFob28nKQpjaGFydFNlcmllcyhRUVEsIHRoZW1lPSd3aGl0ZScsIFRBPSJhZGRTTUEoMTAwKSIpCmBgYAoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQoKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CgpgYGAKCgoKPT09PT09PT09PT09PT09PT09IE5PVEVTIG9uIFN5bnRheCBiZWxvdyBoZXJlCnR3byBvcHRpb25zIGZvciBjb2RlIGluIE1hcmtEb3duCmBgYHtyICwgaW5jbHVkZT1UUlVFfQpnZXRTeW1ib2xzKCdeR1NQQycsIHNyYz0neWFob28nKQpgYGAKCgpgYGB7ciAgfQpnZXRTeW1ib2xzKCdeR1NQQycsIHNyYz0neWFob28nKQpgYGAKCmBgYHtyfQoKYGBgCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCg==